package com.njtoyo.taxi.admin.security.interceptor;

import cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Lists;
import com.njtoyo.taxi.admin.aop.PassToken;
import com.njtoyo.taxi.admin.aop.PermissionScope;
import com.njtoyo.taxi.admin.aop.SensitiveOperation;
import com.njtoyo.taxi.admin.library.Const;
import com.njtoyo.taxi.admin.library.common.SensitiveOperationHelper;
import com.njtoyo.taxi.admin.rest.wrapper.Identity;
import com.njtoyo.taxi.entity.backend.AdminUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;

/**
 * @Author Dell
 * @Date 2021/6/9 17:27
 */
@Slf4j
public class AccessInterceptor implements HandlerInterceptor {

    @Value("${config.permissionVerify}")
    private Boolean permissionVerify;

    @Autowired
    private SensitiveOperationHelper sensitiveOperationHelper;

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {

        httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");

        httpServletResponse.setHeader("Access-Control-Allow-Headers", "Content-Type,Content-Length,Verification-Code, Authorization, Accept,X-Requested-With, Operation-Verify-Content");

        httpServletResponse.setHeader("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS,PATCH");

        httpServletResponse.setHeader("X-Powered-By", "njtoyo");

        String requestMethod = httpServletRequest.getMethod();

        if (Const.REQUEST_OPTIONS_METHOD.equals(requestMethod)) {
            httpServletResponse.setStatus(200);
            return false;
        }

        if (o instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) o;
            Method method = handlerMethod.getMethod();

            if (method.isAnnotationPresent(PassToken.class)) {
                PassToken passToken = method.getAnnotation(PassToken.class);
                if (passToken.required()) {
                    return true;
                }
            }

        }

        if (Objects.nonNull(httpServletRequest.getAttribute(Const.AUTHORIZATION_HEADER))) {
            HandlerMethod handlerMethod = (HandlerMethod) o;
            Method method = handlerMethod.getMethod();
            // 未开启验证，直接通过
            if (!permissionVerify) {
                return true;
            }
            if (method.isAnnotationPresent(PermissionScope.class)) {
                PermissionScope scope = method.getAnnotation(PermissionScope.class);
                List<String> resources = ((Identity) httpServletRequest.getAttribute(Const.AUTHORIZATION_HEADER)).getAdminUser().getResourceNames();
                boolean flag = CollUtil.containsAny(Lists.newArrayList(scope.names()), resources);
                if (!flag) {
                    setForbiddenResponse(httpServletResponse);
                    return false;
                }
            }
            // 敏感授权校验
            if (method.isAnnotationPresent(SensitiveOperation.class)) {
                SensitiveOperation operation = method.getAnnotation(SensitiveOperation.class);
                String operationName = operation.name();
                AdminUser adminUser = ((Identity) httpServletRequest.getAttribute(Const.AUTHORIZATION_HEADER)).getAdminUser();
                SensitiveOperationHelper operationHelper = sensitiveOperationHelper.init(adminUser, operationName);

                if (operationHelper.isNeedVerify()) {
                    String code = httpServletRequest.getHeader("Operation-Verify-Content");
                    if (!operationHelper.verifyCode(code)) {
                        setSensitiveOperationErrorResponse(httpServletResponse, operationName);
                        return false;
                    }
                }
            }
            return true;
        }

        setUnauthorizedResponse(httpServletResponse);
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }


    private void setUnauthorizedResponse(HttpServletResponse httpServletResponse) {
        httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        httpServletResponse.addHeader("WWW-Authenticate",
                String.format("OAuth realm=\"njtoyo\", oauth_error=\"unauthorized\""));
    }

    private void setForbiddenResponse(HttpServletResponse httpServletResponse) {
        httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
        httpServletResponse.addHeader("WWW-Authenticate",
                String.format("OAuth realm=\"njtoyo\", oauth_error=\"forbidden\""));
    }

    private void setSensitiveOperationErrorResponse(HttpServletResponse httpServletResponse, String operationName) {
        httpServletResponse.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED);
        httpServletResponse.addHeader("Operation-Verify-Name", operationName);
    }

}
